#include <cstring>
#include <climits>
#include <cstdlib>
#include <cerrno>
#include <iostream>
#include <getopt.h>
#include "xor_file.h"
using std::string;

extern char *optarg;
extern int optind, opterr, optopt;

static struct option long_options[] =
    {
        {"help", no_argument, NULL, 'h'},
        {"version", no_argument, NULL, 'v'},
        {"passwd", no_argument, NULL, 'p'},
        {"encrypt", no_argument, NULL, 'e'},
        {"decrypt", no_argument, NULL, 'd'},
        {"name", no_argument, NULL, 10001},
        {"name-only", no_argument, NULL, 10002},
        {0, 0, 0, 0}};

const char *const default_passwd = "123456";

const char *const version_info = R"(
program: xxxfile
description: Encrypt or decrypt files using a password.
version: 1.0.0
author: moriv4
email: 985811440@qq.com
)";

const char *const help_info = R"(
xxxfile  Encrypt or decrypt files using a password.

Usage:
 xxxfile [OPTION]... [FILE]...

Options:
 -p, --passwd   password to encrypt or decrypt files
 -e, --encrypt  encrypt files' content
 -d, --decrypt  decrypt files' content   
 -h, --help     display this help and exit
 -v, --version  output version information and exit
 --name         encrypt or decrypt filename after processing files' content
 --name-only    only encrypt or decrypt filename

Examples:
 xxxfile -p passwd -e filepath1 filepath2 filepath3 ...
 xxxfile -p passwd -d filepath1 filepath2 filepath3 ...
 xxxfile --version
 xxxfile --help

Note:
 If filename startswith - (dash), use absolute filepath.

 Backup important files before encryption.
)";

int process_args(int argc, char *const *argv)
{
    if (argc <= 1)
    {
        printf("%s\n", help_info);
        return 0;
    }

    const char *passwd = nullptr;
    int c = 0;     // 用于接收选项
    int index = 0; // 长选项索引
    bool name_flag = false;
    bool name_only_flag = false;

    // 处理选项：帮助、版本、错误选项
    optind = 1;
    while (EOF != (c = getopt_long(argc, argv, "-edpvh", long_options, &index)))
    {
        switch (c)
        {
        case 'h':
            printf("%s\n", help_info);
            return 0;
        case 'v':
            printf("%s\n", version_info);
            return 0;
        case 10001:
            name_flag = true;
            name_only_flag = false;
            break;
        case 10002:
            name_only_flag = true;
            name_flag = false;
            break;
        case '?':
            printf("%s\n", help_info);
            return 1;
        }
    }
    // std::cout << name_flag << name_only_flag << std::endl;
    opterr = 0; // 关闭错误选项输出

    // 获得密码
    optind = 1;
    while (EOF != (c = getopt_long(argc, argv, "-p", long_options, &index)))
    {
        switch (c)
        {
        case 'p':
            if (optind >= argc)
                break;
            passwd = argv[optind++];
            printf("passwd is %s\n", passwd);
            break;
        }
    }
    if (passwd == nullptr)
    {
        passwd = default_passwd;
    }
    // 处理加密解密
    bool operation = false;
    optind = 1;
    char *path;
    while (EOF != (c = getopt_long(argc, argv, "-ed", long_options, &index)))
    {
        switch (c)
        {
        case 'e':
            printf("encrypt files:\n");
            for (int i = optind; i < argc; i++)
            {
                if (argv[i][0] != '-')
                {
                    operation = true;
                    if ((path = realpath(argv[i], nullptr)) != nullptr)
                    {
                        printf("  %s\n", path);
                        if (name_only_flag)
                        {
                            rename_file(path, encrypt_filename(basename(path), passwd));
                        }
                        else if (name_flag)
                        {
                            encrypt_file_content(path, passwd);
                            rename_file(path, encrypt_filename(basename(path), passwd));
                        }
                        else
                        {
                            encrypt_file_content(path, passwd);
                        }
                        free(path);
                        path = nullptr;
                    }
                    else
                    {
                        printf("  %s: %s\n", argv[i], strerror(errno));
                    }
                }
                else
                {
                    optind = i;
                    break;
                }
            }
            break;
        case 'd':
            printf("decrypt files:\n");
            for (int i = optind; i < argc; i++)
            {
                if (argv[i][0] != '-')
                {
                    operation = true;
                    if ((path = realpath(argv[i], nullptr)) != nullptr)
                    {
                        printf("  %s\n", path);
                        if (name_only_flag)
                        {
                            rename_file(path, decrypt_filename(basename(path), passwd));
                        }
                        else if (name_flag)
                        {
                            decrypt_file_content(path, passwd);
                            rename_file(path, decrypt_filename(basename(path), passwd));
                        }
                        else
                        {
                            decrypt_file_content(path, passwd);
                        }
                        free(path);
                        path = nullptr;
                    }
                    else
                    {
                        printf("  %s: %s\n", argv[i], strerror(errno));
                    }
                }
                else
                {
                    optind = i;
                    break;
                }
            }
            break;
        }
    }
    if (!operation)
    {
        printf("%s\n", help_info);
    }
    return 0;
}

int main(int argc, char *const *argv)
{
    // rename_file("/home/jzc/Programs/Test/xxxfile/test/123.txt", "456.txt");
    // return 0;
    return process_args(argc, argv);
}